#ifndef B2D_RESOURCEFACTORY_H
#define B2D_RESOURCEFACTORY_H

#include "IResourceFactory.h"
#include "IFileSystem.h"
#include "IContext.h"
#include "hashmap.h"
#include <mutex>
#include <atomic>

NS_B2D_BEGIN

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Base implmentation of IResourceFactory. It justs leaves the actual instantiation of
/// 		the resource and type of the resource to derived to factories that derive from this
/// 		class.
////////////////////////////////////////////////////////////////////////////////////////////////////
class B2D_API BaseResourceFactory
	: public IResourceFactory
{
private:
	struct ResourceInfo
	{
		enum { LOADED, LOADING, FAILED };
		ResourcePtr resource;
		std::atomic_char state;
	};
	typedef hashmap<StringId, ResourceInfo> ResourceCache;

	ResourceCache mCache;
	mutable std::mutex mCacheMutex;

public:
	virtual ~BaseResourceFactory(void) { }

	// ::IResourceFactory
	size_t getResourceCount(void) const { return mCache.size(); }
	ResourcePtr findResource(const StringId& name) const;

_internal:
	// ::IResourceFactory
	void _addResourceToCache(const ResourcePtr& r);
	void _removeResourceFromCache(const StringId& id);
	ResourcePtr _createEmpty(const string& name, bool addToCache);
	ResourcePtr _createFromFile(const string& path, bool addToCache);
	ResourcePtr _getOrCreateFromFile(const string& path);
	void _clearCache(void);
	void _clearUnreferenced(void);

protected:
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Create an empty resource.
	///
	/// \param	name		The name.
	/// \param	fromFile	Resource created to be loaded from file.
	///
	/// \return	The new empty resource.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual ResourcePtr createEmpty(void) = 0;
};

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Generic implementation of BaseResourceFactory with the specific type of resource. 
///
/// \tparam	T	Type of resource to handle.
////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename T>
class B2D_API ResourceFactory
	: public BaseResourceFactory
{
	static_assert(std::is_base_of<IResource,T>::value,
			"The resource type must derive from IResource.");
public:
	virtual ~ResourceFactory(void) { }

	// :: IResourceFactory
	const ObjectType& getResourceType(void) const { return T::ResourceType; }
};

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Default resource factory. It simply creates a new instance of the type. There are two
/// 		template parameters to allow instantiating a type that derives from T when the actual
/// 		type needs to be hidden, ie DefaultResourceFactory<MyResource,Win32MyResource> to hide
/// 		the specific platform implementation. For convienience TActual defaults to T.
///
/// \tparam	T	Type of resource.
/// \tparam	T	Actual type to instantiate.
////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename T, typename TActual = T>
class B2D_API DefaultResourceFactory final
	: public ResourceFactory<T>
{
	static_assert(std::is_base_of<T,TActual>::value,
			"The actual type to instantiate must derive from the factory resource type.");
protected:
	// :: BaseResourceFactory
	ResourcePtr createEmpty(void)
	{
		T* res = new TActual();
		return ResourcePtr(res);
	}
};

NS_B2D_END

#endif